package com.alstudio.module.xmpp.protocol.xmpp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

import android.text.TextUtils;

import com.alstudio.module.xmpp.protocol.xmpp.data.ALXmppConnectionState;
import com.alstudio.module.xmpp.protocol.xmpp.data.ALXmppData;
import com.alstudio.module.xmpp.protocol.xmpp.packact.ALIQ;
import com.alstudio.module.xmpp.protocol.xmpp.packact.ALIQType;
import com.alstudio.module.xmpp.protocol.xmpp.packact.ALMessage;
import com.alstudio.module.xmpp.protocol.xmpp.packact.IQNameSpace;
import com.alstudio.module.xmpp.protocol.xmpp.packact.Presence;
import com.alstudio.module.xmpp.protocol.xmpp.packact.Presence.PresenceType;
import com.alstudio.module.xmpp.protocol.xmpp.util.PackactIdGenerator;
import com.alstudio.utils.encode.ALDES;
import com.alstudio.utils.log.ALLog;

/**
 * XMPP Protocol
 * 
 * @author alonso lee
 */

public class ALXmppProtocol extends Thread {

	// 读取服务器下发的字符流
	private ALXmlReader reader;
	// 写字符流到服务器控制器
	private ALXmlWriter writer;
	// 与服务器间的输入流，用于初始化reader
	private InputStream is;
	// 与服务器间的输出流，用于初始化writer
	private OutputStream os;
	// 创建与服务器间的连接
	private Socket socket;
	// 本实例需要用到的数据
	private ALXmppData mData;

	// 当前登陆状态，缺省状态为连接断开
	private static ALXmppConnectionState connectionState = ALXmppConnectionState.XMPP_STATE_IDLE;

	/* 此标志位用于确认当前是否已经登录到服务器.true标示已经登录，false则否 */
	private boolean authenticated = false;

	private ALXmppEventListener listener = null;

	private ALXmppParseConfigue mParseConfigure;

	// 当前xmpp session id
	private String sid;

	public ALXmppProtocol(ALXmppData data, ALXmppEventListener listener) {
		mData = data;
		this.listener = listener;
		mParseConfigure = new ALXmppParseConfigue();
	}

	/**
	 * 设定连接参数
	 * 
	 * @param data
	 * @author alonso lee
	 */
	public void setXmppData(ALXmppData data) {
		mData = data;
	}

	/**
	 * 获取连接参数
	 * 
	 * @return
	 * @author alonso lee
	 */
	public ALXmppData getXmppData() {
		return this.mData;
	}

	/**
	 * 获取当前连接状态
	 * 
	 * @return
	 * @author alonso lee
	 */
	public static ALXmppConnectionState getConnectionState() {
		return connectionState;
	}

	/**
	 * 更改当前连接状态，并输出调试信息
	 * 
	 * @author alonso lee
	 */
	public synchronized void setConnectionState(ALXmppConnectionState newState) {
		String oldState = connectionState2String();
		ALXmppConnectionState lastState = connectionState;
		connectionState = newState;
		ALLog.e("connection state change to " + connectionState2String()
				+ " last state is " + oldState);
		listener.onConnectStateChangedEvent(newState, lastState);
	}

	/**
	 * 
	 * @return 获取当前连接状态的字符串描述
	 * @author alonso lee
	 */
	public static String connectionState2String() {
		String state = "";

		switch (connectionState) {
		case XMPP_STATE_IDLE:
			state = "offline";
			break;
		case XMPP_STATE_CONNECTING_SOCKET:
			state = "connecting socket...";
			break;
		case XMPP_STATE_SOCKET_CONNECTED:
			state = "socket connected";
			break;
		case XMPP_STATE_OPENNING_STREAM:
			state = "opening stream to server...";
			break;
		case XMPP_STATE_CHECKING_LOGIN_PROTOCOL:
			state = "verifying...";
			break;
		case XMPP_STATE_CONNECTION_ESTABLISHED:
			state = "Logged in";
			break;
		}

		return state;
	}

	/**
	 * 此方法仅供此类输出接收到得xmpp消息
	 * 
	 * @author alonso lee
	 */
	private void printRecvMsg() {
		ALLog.d("RECV: " + reader.buf.toString());
		reader.buf.setLength(0);
	}

	/**
	 * The <code>run</code> method is called when {@link Jxa} object is created.
	 * It sets up the reader and writer, calls {@link #login()} methode and
	 * listens on the reader to parse incomming xml stanzas.
	 */
	public void run() {

		if (getConnectionState() != ALXmppConnectionState.XMPP_STATE_IDLE) {
			ALLog.w("正在连接中");
			return;
		}

		this.authenticated = false;

		try {
			// 正在连接socket
			setConnectionState(ALXmppConnectionState.XMPP_STATE_CONNECTING_SOCKET);

			InetSocketAddress sa = new InetSocketAddress(mData.getHost(),
					Integer.parseInt(mData.getPort()));
			socket = new Socket();

			/* 设定超时时间为10S */
			socket.connect(sa, 10000);

			is = socket.getInputStream();
			os = socket.getOutputStream();
			this.reader = new ALXmlReader(is);
			this.writer = new ALXmlWriter(os);
		} catch (final IOException e) {
			ALLog.d("Connect To Server Failed: " + e.toString());
			// 打开失败，恢复缺省状态
			setConnectionState(ALXmppConnectionState.XMPP_STATE_IDLE);
			this.connectionFailed(e.toString());
			return;
		}

		// 已经连接socket了
		setConnectionState(ALXmppConnectionState.XMPP_STATE_SOCKET_CONNECTED);

		// 根据设定的工作模式跳转到指定的方法！
		try {
			switch (mData.getConnectionMode()) {
			case ALXmppData.MODE_LOGIN:
				/* 现在开始登录 */
				login();
				break;
			case ALXmppData.MODE_REGISTER:
				/* 获取账号密码 */
				break;
			}

			/* 开启解析器！无限循环读取消息 */
			this.parse();

			ALLog.e("连接中断");

			if (reader.buf.toString().contains("conflict")) {
				// 被踢下线了
				if (connectionState == ALXmppConnectionState.XMPP_STATE_CONNECTION_ESTABLISHED) {
					setConnectionState(ALXmppConnectionState.XMPP_STATE_IDLE);
					String conflictSid = getConflictSessionId(reader.buf
							.toString());
					this.connectionFailed("conflict", conflictSid);
				}
			} else {
				setConnectionState(ALXmppConnectionState.XMPP_STATE_IDLE);
				this.connectionFailed();
			}
			this.printRecvMsg();
		} catch (final IOException e) {
			ALLog.e("Connection Close: " + e.toString());
			if (e.toString().contains("Invalid argument")) {
				logoff();
			}
			setConnectionState(ALXmppConnectionState.XMPP_STATE_IDLE);
			this.connectionFailed(e.toString());
		}
	}

	/**
	 * Add a {@link ALXmppListener} to listen for events.
	 * 
	 * @param xl
	 *            a ALXmppListener object
	 */
	public void addListener(final ALXmppEventListener xl) {
		this.listener = xl;
	}

	/**
	 * Remove a {@link ALXmppListener} from this class.
	 * 
	 * @param xl
	 *            a ALXmppListener object
	 */
	public void removeListener(final ALXmppEventListener xl) {
		listener = null;
	}

	/**
	 * 此方法用于解析服务器返回的打开流。
	 * 
	 * @return
	 * @author alonso lee
	 */
	int t = -1;

	private boolean parseOpenStream() {
		int c = -1;
		while (true) {
			t = c;
			try {
				c = reader.getNextCharacter();
			} catch (IOException e) {
				e.printStackTrace();
				return false;
			}

			if (c != -1) {
				if (c == '>' && t == '\'') {
					ALLog.d("open stream parse completed");
					getSessionId(reader.buf.toString());
					return true;
				}
			}
		}
	}

	private String getSessionId(String s) {
		String id = null;
		try {
			if (s.contains("id")) {
				int index = s.indexOf("id='") + 4;
				id = s.substring(index);
				id = id.substring(0, id.indexOf("'"));
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		sid = id;
		ALLog.d("流id为" + sid);
		listener.onSessionIdUpdateEvent(sid);
		return id;
	}

	private String getConflictSessionId(String s) {
		String id = null;
		if (s.contains("sid")) {
			s = s.substring(s.indexOf("<sid>") + 5, s.indexOf("</sid>"));
			id = s;
		}
		return id;
	}

	/**
	 * 注销登录IQ
	 * 
	 * @param
	 * @throws IOException
	 * @author alonso lee
	 */
	private void logoutIq() throws IOException {
		ALXMLGenerator xml = new ALXMLGenerator();
		xml.startTag("iq");

		xml.attribute("to", "customaccount." + mData.getDomain());
		xml.attribute("type", "set");
		xml.attribute("id", PackactIdGenerator.getPackactIdGenerator()
				.getPackactId(8));
		xml.startTag("query");
		xml.attribute("xmlns", "jabber:moblove:logout");
		xml.endTag(); // end of query

		xml.endTag(); // end of iq
		this.writer.write(xml.getXml());
	}

	/**
	 * 反馈意见IQ
	 * 
	 * @param comment
	 * @param email
	 * @author alonso lee
	 */
	private boolean feedBackMsg(String comment, String email) {
		ALXMLGenerator xml = new ALXMLGenerator();

		try {
			xml.startTag("iq");

			xml.attribute("type", "set");
			xml.attribute("to", "suggest." + mData.getDomain());
			xml.attribute("id", PackactIdGenerator.getPackactIdGenerator()
					.getPackactId(8));

			xml.startTag("query");
			xml.attribute("xmlns", "jabber:iq:suggest");

			xml.startTag("body");
			xml.text(comment);
			xml.endTag(); // end of body

			xml.startTag("version");
			xml.text(mData.getDevInfo().getClientVersion());
			xml.endTag();

			xml.startTag("os");
			xml.text("Android");
			xml.endTag();

			xml.startTag("model");
			xml.text(mData.getDevInfo().getDeviceModel());
			xml.endTag();

			xml.startTag("downfrom");
			xml.text(mData.getDevInfo().getDownloadSource());
			xml.endTag();

			xml.startTag("firmware");
			xml.text(mData.getDevInfo().getSdkVersion());
			xml.endTag();

			xml.startTag("email");
			xml.text(email);
			xml.endTag();

			xml.endTag(); // end of query
			xml.endTag(); // end of iq

			this.writer.write(xml.getXml());

		} catch (final IOException e) {
			// e.printStackTrace();
			ALLog.e("Send Feedback Failed: " + e.toString());
			// 回调函数通知上层，连接已出现异常。
			this.connectionFailed(e.toString());
			return false;
		}
		return true;
	}

	/**
	 * 注销登录
	 * 
	 * @return true 注销请求发送成功
	 * @author alonso lee
	 */
	public boolean logout() {
		try {
			logoutIq();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		return true;
	}

	/**
	 * 发送反馈信息
	 * 
	 * @param comment
	 * @param email
	 * @author alonso lee
	 */
	public boolean sendFeedBack(String comment, String email) {
		return feedBackMsg(comment, email);
	}

	/**
	 * 拼装响应服务器下发的S2C心跳包
	 * 
	 * @param id
	 * @param from
	 * @param to
	 * @throws IOException
	 * @author alonso lee
	 */
	private void sendPingResponseIq(String id, String from, String to)
			throws IOException {
		ALXMLGenerator xml = new ALXMLGenerator();

		xml.startTag("iq");
		xml.attribute("type", "result");
		xml.attribute("id", id);
		xml.attribute("to", from);
		xml.attribute("from", to);
		xml.endTag(); // iq
		this.writer.write(xml.getXml());
	}

	/**
	 * 拼装C2S心跳包。
	 * 
	 * @throws IOException
	 * @author alonso lee
	 */
	private void sendPingRequestIq() throws IOException {
		ALXMLGenerator xml = new ALXMLGenerator();

		xml.startTag("iq");
		xml.attribute("type", "get");
		xml.attribute("id", PackactIdGenerator.getPackactIdGenerator()
				.getPackactId(5));
		xml.attribute("to", mData.getDomain());
		xml.attribute("from", mData.getUsername() + "@" + mData.getDomain());

		xml.startTag("ping");
		xml.attribute("xmlns", "urn:xmpp:ping");

		xml.endTag(); // end of ping
		xml.endTag(); // iq
		this.writer.write(xml.getXml());

	}

	/**
	 * 发送C2S心跳包，检测本机是否在线。
	 * 
	 * @return
	 * @author alonso lee
	 */
	public boolean sendPingRequest() {
		try {
			sendPingRequestIq();
		} catch (IOException e) {
			e.printStackTrace();
			// 回调函数通知上层，连接已出现异常。
			this.connectionFailed(e.toString());
			return false;
		}
		return true;
	}

	/**
	 * 响应服务器下发的S2C心跳包
	 * 
	 * @return
	 * @author alonso lee
	 */
	public boolean sendPingResponse(String id, String from, String to) {

		try {
			sendPingResponseIq(id, from, to);
		} catch (IOException e) {
			e.printStackTrace();
			// 回调函数通知上层，连接已出现异常。
			this.connectionFailed(e.toString());
			return false;
		}
		return true;
	}

	/**
	 * 执行登陆过程
	 * 
	 * @throws IOException
	 */
	public void login() throws IOException {

		String timeStamp = System.currentTimeMillis() + "";
		String deviceId = null;

		if (mData.getDevInfo() != null) {
			if (TextUtils.isEmpty(mData.getDevInfo().getIMEI())) {

				ALLog.d("imei信息为空 现在检测uuid");

				if (!TextUtils.isEmpty(mData.getDevInfo().getUuid())) {
					deviceId = mData.getDevInfo().getUuid();
					ALLog.d("uuid信息不为空 " + deviceId);
				}
			} else {
				deviceId = mData.getDevInfo().getIMEI();
				ALLog.d("imei信息不为空 " + deviceId);

			}
		}

		String msg = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='"
				+ mData.getDomain();

		msg += "' timestamp='" + timeStamp + "'";

		if (!TextUtils.isEmpty(deviceId)) {
			msg += " deviceid='" + deviceId + "'";
		}

		msg += " version='1.0'>";

		os.write(msg.getBytes());
		os.flush();

		// 正在打开流
		setConnectionState(ALXmppConnectionState.XMPP_STATE_OPENNING_STREAM);

		ALLog.d("SEND: " + msg);

		/* 解析服务器打开流 */
		parseOpenStream();

		// 打印接收到的XML信息
		printRecvMsg();

		// 开始做登陆校验
		setConnectionState(ALXmppConnectionState.XMPP_STATE_CHECKING_LOGIN_PROTOCOL);

		/* 开始校验 */
		msg = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'><verify>";
		try {
			msg += ALDES.encryptDES(
					"\0" + mData.getUsername() + "\0" + mData.getPassword(),
					ALXmppData.DES_KEY) + "</verify>";
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		msg = msg + "<resource>" + "android-" + mData.getNetType()
				+ "</resource>";

		// 客户端IMEI
		try {
			msg += "<uniqueid>"
					+ ALDES.encryptDES(mData.getDevInfo().getIMEI(),
							ALXmppData.DES_KEY) + "</uniqueid>";
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		msg = msg + "<type>" + mData.getAuthType() + "</type>"; // 2011-10-10
		// 带上客户端版本号
		msg = msg + "<version>" + mData.getAppVersion() + "</version>"; // 2013-6-19

		// 带上客户端版本号
		msg = msg + "<version>" + mData.getAppVersion() + "</version>"; // 2013-6-19
		
		msg = msg + "</auth>";

		os.write(msg.getBytes());
		os.flush();

		ALLog.d("SEND: " + msg);
		String token = "";
		String username = "";
		reader.next();
		if (reader.getName().equals("success")) {
			String tagname = null;
			while (true) {
				reader.next();
				tagname = reader.getName();
				if (tagname != null) {

					if ((reader.getType() == ALXmlReader.START_TAG)
							&& tagname.equals("token")) {
						token = parseText();
						ALLog.d("token是 " + token);
					} else if (reader.getType() == ALXmlReader.START_TAG
							&& tagname.equals("jid")) {
						// ALLog.d("登陆成功!!");
						username = parseText();
						if (username.indexOf("@") != -1) {
							username = username.substring(0,
									username.indexOf("@"));
						}
						ALLog.d("登陆成功的账号 " + username);
					} else if (tagname.equals("success")) {
						ALLog.d("登陆成功!!");
						break;
					}
				}
			}
			// 打印接收到的XML信息
			printRecvMsg();

			this.authenticated = true;

			// 已经登录到服务器了！
			setConnectionState(ALXmppConnectionState.XMPP_STATE_CONNECTION_ESTABLISHED);

			// 回调通知中间件，已经登陆成功了
			listener.onLoginedEvent(username, mData.getPassword(), token);

		} else {

			this.parseIgnore();

			// 打印接收到的XML信息
			printRecvMsg();

			// 恢复缺省状态
			setConnectionState(ALXmppConnectionState.XMPP_STATE_IDLE);

			// 登录失败了哦
			listener.onAuthFailedEvent("failed authentication");

			this.authenticated = false;

			logoff();
			return;
		}
	}

	/**
	 * 发送刘关闭，断开连接
	 */
	public void logoff() {

		try {
			if (os != null) {
				String msg = "</stream:stream>";
				// 写关闭流！
				os.write(msg.getBytes());
				os.flush();
				ALLog.d("SEND: " + msg);
			}

			// 关闭所有打开的通道
			if (this.writer != null) {
				this.writer.close();
			}

			if (this.reader != null) {
				this.reader.close();
			}

			if (this.socket != null) {
				this.socket.close();
			}

		} catch (final IOException e) {
			ALLog.e("logoff Failed: " + e.toString());
			// 关闭流出现问题，强制更改状态
			setConnectionState(ALXmppConnectionState.XMPP_STATE_IDLE);
			this.connectionFailed(e.toString());
		} finally {
			// 关闭所有打开的通道
			if (this.writer != null) {
				this.writer.close();
			}

			if (this.reader != null) {
				this.reader.close();
			}

			try {
				if (this.socket != null) {
					this.socket.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	/**
	 * Sends a message text to a known jid.
	 * 
	 * @param to
	 *            the JID of the recipient
	 * @param msg
	 *            the message itself
	 */
	public boolean sendMessage(ALMessage msg) {
		ALXMLGenerator xml = new ALXMLGenerator();

		try {
			xml.startTag("message");
			switch (msg.getType()) {
			case ALMessage.TEXT_MSG:
				xml.attribute("type", "text");
				break;
			case ALMessage.AUDIO_FACE_MSG:
				xml.attribute("type", "audioface");
				break;
			case ALMessage.IMAGE_MSG:
				xml.attribute("type", "image");
				break;
			case ALMessage.AUDIO_LEAVE:
				// 语音留言
				xml.attribute("type", "audioleave");
				break;
			case ALMessage.CALL_IMAGE_MSG:
				// 通话中的图片消息
				xml.attribute("type", "tmp-image");
				break;
			}
			xml.attribute("to", msg.getTo());
			xml.attribute("fromnick", msg.getFromNick());
			xml.attribute("id", msg.getMsgId());
			xml.attribute("fromavatar", msg.getFromAvatar());

			if (msg.getCc() != null) {
				StringBuffer buf = new StringBuffer();

				for (int i = 0; i < msg.getCc().size(); i++) {
					if (i > 0) {
						buf.append(",");
					}
					buf.append(msg.getCc().get(i));
				}

				xml.attribute("cc", buf.toString());
			}

			switch (msg.getType()) {
			case ALMessage.AUDIO_FACE_MSG:
				xml.startTag("url");
				xml.text(msg.getAudioFaceId());
				xml.endTag(); // end of url
				break;
			case ALMessage.AUDIO_LEAVE:
			case ALMessage.IMAGE_MSG:
			case ALMessage.CALL_IMAGE_MSG:
				xml.startTag("url");
				xml.text(msg.getFIleId());
				xml.endTag(); // end of url
				break;
			}

			if (msg.getType() == ALMessage.AUDIO_LEAVE) {
				if (!TextUtils.isEmpty(msg.getSex())) {
					xml.startTag("sex");
					xml.text(msg.getSex());
					xml.endTag(); // end of sex
				}

				if (!TextUtils.isEmpty(msg.getVauth())) {
					xml.startTag("vauthed");
					xml.text(msg.getVauth());
					xml.endTag(); // end of vauthed
				}
			}

			xml.startTag("body");
			if (!TextUtils.isEmpty(msg.getBob())) {
				xml.text(msg.getBob());
			} else {
				xml.text(msg.getBody());
			}
			xml.endTag(); // end of body

			xml.endTag(); // end of message

			this.writer.write(xml.getXml());

		} catch (final IOException e) {
			// e.printStackTrace();
			ALLog.e("Send ALMessage Failed: " + e.toString());
			this.connectionFailed();
			return false;
		}

		return true;
	}

	/**
	 * 发送消息回执！
	 * 
	 * @param from
	 * @param to
	 * @param id
	 * @param type
	 * @author alonso lee
	 */
	public boolean sendReceipt(String to, String id, String type) {
		ALXMLGenerator xml = new ALXMLGenerator();

		try {
			xml.startTag("message");
			xml.attribute("from", mData.getUsername() + "@" + mData.getDomain());
			xml.attribute("to", to);
			xml.startTag("x");
			xml.attribute("xmlns", "jabber:x:event");
			xml.startTag("id");
			xml.text(id);
			xml.endTag();

			xml.startTag(type);
			xml.endTag();
			xml.endTag();
			xml.endTag();

			this.writer.write(xml.getXml());

		} catch (final IOException e) {
			// e.printStackTrace();
			ALLog.e("Send Receipt Fialed: " + e.toString());
			// 回调函数通知上层，连接已出现异常。
			this.connectionFailed(e.toString());
			return false;
		}
		return true;
	}

	/**
	 * 发送iq回执给服务器
	 * 
	 * @param id
	 * @throws IOException
	 */
	private void sendIqResultWithNs(String id, String from, String ns)
			throws IOException {
		ALXMLGenerator xml = new ALXMLGenerator();

		xml.startTag("iq");
		xml.attribute("type", "result");
		xml.attribute("id", id);
		xml.attribute("to", from);
		if (!TextUtils.isEmpty(ns)) {
			xml.startTag("query");
			xml.attribute("xmlns", ns);
			xml.endTag(); // query
		}
		xml.endTag(); // iq
		this.writer.write(xml.getXml());
	}

	/**
	 * Sends a presence stanza to a jid. This method can do various task but
	 * it's private, please use setStatus to set your status or explicit
	 * subscription methods subscribe, unsubscribe, subscribed and unsubscribed
	 * to change subscriptions.
	 */
	private boolean sendPresence(final String to, final String type,
			final String show, final String status, final int priority) {

		ALXMLGenerator xml = new ALXMLGenerator();

		try {
			xml.startTag("presence");
			if (type != null) {
				xml.attribute("type", type);
			}
			if (to != null) {
				xml.attribute("to", to);
			}
			if (show != null) {
				xml.startTag("show");
				xml.text(show);
				xml.endTag();
			}
			if (status != null) {
				xml.startTag("status");
				xml.text(status);
				xml.endTag();
			}
			if (priority != 0) {
				xml.startTag("priority");
				xml.text(Integer.toString(priority));
				xml.endTag();
			}
			xml.endTag(); // presence
			this.writer.write(xml.getXml());

		} catch (final IOException e) {
			ALLog.e("Send Presence Failed: " + e.toString());
			this.connectionFailed();
			return false;
		}
		return true;
	}

	/**
	 * Sets your Jabber Status.
	 * 
	 * @param show
	 *            is one of the following: <code>null</code>, chat, away, dnd,
	 *            xa, invisible
	 * @param status
	 *            an extended text describing the actual status
	 * @param priority
	 *            the priority number (5 should be default)
	 */
	public void setStatus(String show, String status, final int priority) {
		this.sendPresence(null, null, show, status, priority);
	}

	/**
	 * Requesting a subscription.
	 * 
	 * @param to
	 *            the jid you want to subscribe
	 */
	public boolean subscribe(final String to) {
		return this.sendPresence(to, "subscribe", null, null, 0);
	}

	/**
	 * Remove a subscription.
	 * 
	 * @param to
	 *            the jid you want to remove your subscription
	 */
	public boolean unSubscribe(final String to) {
		return this.sendPresence(to, "unsubscribe", null, null, 0);
	}

	/**
	 * Approve a subscription request.
	 * 
	 * @param to
	 *            the jid that sent you a subscription request
	 */
	public void subscribed(final String to) {
		this.sendPresence(to, "subscribed", null, null, 0);
	}

	/**
	 * Refuse/Reject a subscription request.
	 * 
	 * @param to
	 *            the jid that sent you a subscription request
	 */
	public void unsubscribed(final String to) {
		this.sendPresence(to, "unsubscribed", null, null, 0);
	}

	/**
	 * Sends a roster query.
	 * 
	 * @throws java.io.IOException
	 *             is thrown if {@link ALXmlReader} or {@link ALXmlWriter} throw
	 *             an IOException.
	 */
	public void getRoster() throws IOException {
		ALXMLGenerator xml = new ALXMLGenerator();

		xml.startTag("iq");
		xml.attribute("id", PackactIdGenerator.getPackactIdGenerator()
				.getPackactId(8));
		xml.attribute("type", "get");
		xml.startTag("query");
		xml.attribute("xmlns", "jabber:iq:roster");
		xml.endTag(); // query
		xml.endTag(); // iq

		this.writer.write(xml.getXml());
	}

	/**
	 * 发送iq回执给服务器
	 * 
	 * @param id
	 * @throws IOException
	 */
	private void sendIqResult(String id, String from) throws IOException {
		ALXMLGenerator xml = new ALXMLGenerator();

		xml.startTag("iq");
		xml.attribute("type", "result");
		xml.attribute("id", id);
		xml.attribute("to", from);
		xml.endTag(); // iq
		this.writer.write(xml.getXml());
	}

	/**
	 * 开始解析
	 * 
	 * @throws IOException
	 */
	private void parse() throws IOException {

		while (this.reader.next() == ALXmlReader.START_TAG) {
			final String tmp = this.reader.getName();

			if (tmp.equals("message")) {
				this.parseMessage();
			} else if (tmp.equals("presence")) {
				this.parsePresence();
			} else if (tmp.equals("iq")) {
				this.parseIq();
			} else {
				this.parseIgnore();
			}
		}

		ALLog.e("leave parse() " + reader.getName());
		this.reader.close();

	}

	/**
	 * This method parses all info/query stanzas, including authentication
	 * mechanism and roster. It also answers version queries.
	 * 
	 * @throws java.io.IOException
	 *             is thrown if {@link ALXmlReader} or {@link ALXmlWriter} throw
	 *             an IOException.
	 */
	private void parseIq() throws IOException {

		String type = this.reader.getAttribute("type");
		final String id = this.reader.getAttribute("id");
		final String from = this.reader.getAttribute("from");
		String to = this.reader.getAttribute("to");
		String ns = "";
		ALIQ iqPackact = new ALIQ(from, to, id);
		iqPackact.setType(getIQType(type));

		while (true) {
			reader.next();
			String tagname = reader.getName();
			if (tagname != null) {
				if ((reader.getType() == ALXmlReader.START_TAG)
						&& tagname.equals("query")) {
					ns = reader.getAttribute("xmlns");
					iqPackact.setNameSpace(ns);
				} else if ((reader.getType() == ALXmlReader.END_TAG)
						&& tagname.equals("ping")) {
					ns = reader.getAttribute("xmlns");
					iqPackact.setNameSpace(ns);

				} else if ((reader.getType() == ALXmlReader.END_TAG)
						&& tagname.equals("iq")) {
					break;
				}
			}
		}

		if ("set".equals(type)) {
			// set类型的数据，发回执给服务器
			sendIqResultWithNs(id, from, ns);
		} else if ("get".equals(type)
				&& ns.equals(IQNameSpace.NS_URN_XMPP_PING)) {
			sendPingResponse(id, from, to);
			ALLog.d("响应服务器的ping包");
		}

		mParseConfigure.getIQParser(iqPackact, reader.buf.toString(), listener);
		printRecvMsg();
	}

	/**
	 * This method parses all presence stanzas, including subscription requests.
	 * 
	 * @throws java.io.IOException
	 *             is thrown if {@link ALXmlReader} or {@link ALXmlWriter} throw
	 *             an IOException.
	 */
	private void parsePresence() throws IOException {

		final String type = this.reader.getAttribute("type");
		Presence presence = new Presence(getPresenceType(type));
		presence.setFrom(this.reader.getAttribute("from"));
		this.parseIgnore();

		mParseConfigure.getALPresenceParser(presence, reader.buf.toString(),
				listener);

		// 输出收到的XML信息
		printRecvMsg();

		if (type == null) {
			// listener.onJxaStatusEvent(from, "online", null);
			/**************
			 * cardboxing项目暂时用不到订阅状态。关闭*********** for (Enumeration e =
			 * listeners.elements(); e.hasMoreElements();) { ALXmppListener xl =
			 * (ALXmppListener) e.nextElement(); xl.onJxaStatusEvent(from,
			 * "online", status); }
			 */
		} else { // type != null
			if (type.equals("unsubscribed") || type.equals("error")) {

				/**************
				 * cardboxing项目暂时用不到订阅状态。关闭*********** for (Enumeration e =
				 * listeners.elements(); e.hasMoreElements();) { ALXmppListener
				 * xl = (ALXmppListener) e.nextElement();
				 * xl.onJxaUnsubscribeEvent(from); }
				 */

			} else if (type.equals("subscribe")) {
				/**************
				 * cardboxing项目暂时用不到订阅状态。关闭*********** for (Enumeration e =
				 * listeners.elements(); e.hasMoreElements();) { ALXmppListener
				 * xl = (ALXmppListener) e.nextElement();
				 * xl.onJxaSubscribeEvent(from); }
				 */
			} else if (type.equals("unavailable")) {
				/**************
				 * cardboxing项目暂时用不到订阅状态。关闭*********** for (Enumeration e =
				 * listeners.elements(); e.hasMoreElements();) { ALXmppListener
				 * xl = (ALXmppListener) e.nextElement();
				 * 
				 * xl.onJxaStatusEvent(from, "offline", status); }
				 */
				// listener.onJxaStatusEvent(from, "offline", null);
			}
			// end type.equals
		} // end type == null
	}

	/**
	 * This method parses all incoming messages.
	 * 
	 * @throws java.io.IOException
	 *             is thrown if {@link ALXmlReader} or {@link ALXmlWriter} throw
	 *             an IOException.
	 */
	private void parseMessage() throws IOException {

		final String from = this.reader.getAttribute("from"), type = this.reader
				.getAttribute("type");
		String id = this.reader.getAttribute("id");

		ALMessage msg = new ALMessage();
		msg.setFrom(from);
		msg.setMsgId(id);
		msg.setFromNick(reader.getAttribute("fromnick"));
		msg.setFromAvatar(reader.getAttribute("fromavatar"));
		msg.setMsgType(type);

		this.parseIgnore();

		if (!TextUtils.isEmpty(type)) {
			// 这里要加入判断，根据类型来判断是否要发回执。现在统一发回执
			if (!type.equals("error")) {
				// 类型不为错误，直接回执
				sendReceipt(from, id, "delivered");
			}
		}
		mParseConfigure.getMessageParser(msg, reader.buf.toString(), listener);

		// 输出收到的XML信息
		printRecvMsg();
	}

	/**
	 * This method parses all text inside of xml start and end tags.
	 * 
	 * @throws java.io.IOException
	 *             is thrown if {@link XmlReader} or {@link XmlWriter} throw an
	 *             IOException.
	 */
	private String parseText() throws IOException {
		final String endTagName = this.reader.getName();
		final StringBuffer str = new StringBuffer("");
		int t = this.reader.next(); // omit start tag
		while (!endTagName.equals(this.reader.getName())) {
			if (t == ALXmlReader.TEXT) {
				str.append(this.reader.getText());
			}
			t = this.reader.next();
		}
		return str.toString();
	}

	/**
	 * This method doesn't parse tags it only let the reader go through unknown
	 * tags.
	 * 
	 * @throws java.io.IOException
	 *             is thrown if {@link ALXmlReader} or {@link ALXmlWriter} throw
	 *             an IOException.
	 */
	private void parseIgnore() throws IOException {
		int x;
		while ((x = this.reader.next()) != ALXmlReader.END_TAG) {
			if (x == ALXmlReader.START_TAG) {
				this.parseIgnore();
			}
		}
	}

	private void connectionFailed(final String msg) {
		this.authenticated = false;
		if (this.writer != null) {
			this.writer.close();
		}

		if (this.reader != null) {
			this.reader.close();
		}

		if (this.socket != null)
			try {
				this.socket.close();
			} catch (IOException e1) {
				e1.printStackTrace();
				ALLog.e("In Callback Close Socket Failed: " + e1.toString());
			}
		listener.onConnectLostEvent(msg);
	}

	/**
	 * 将iq的字符串转为IQType
	 * 
	 * @param type
	 * @return
	 */
	private ALIQType getIQType(String type) {
		ALIQType mType = null;
		if (!TextUtils.isEmpty(type)) {
			if (type.equals("error")) {
				mType = ALIQType.ERROR;
			} else if (type.equals("set")) {
				mType = ALIQType.SET;
			} else if (type.equals("get")) {
				mType = ALIQType.GET;
			} else if (type.equals("result")) {
				mType = ALIQType.RESULT;
			}
			return mType;
		}
		return null;
	}

	private PresenceType getPresenceType(String type) {
		PresenceType mType = PresenceType.available;
		if (!TextUtils.isEmpty(type)) {

			if (type.equals("available")) {
				mType = PresenceType.available;
			} else if (type.equals("error")) {
				mType = PresenceType.error;
			} else if (type.equals("subscribe")) {
				mType = PresenceType.subscribe;
			} else if (type.equals("subscribed")) {
				mType = PresenceType.subscribed;
			} else if (type.equals("unavailable")) {
				mType = PresenceType.unavailable;
			} else if (type.equals("unsubscribe")) {
				mType = PresenceType.unsubscribe;
			} else if (type.equals("unsubscribed")) {
				mType = PresenceType.unsubscribed;
			}
		}
		return mType;
	}

	/**
	 * This method is used to be called on a parser or a connection error. It
	 * tries to close the XML-Reader and XML-Writer one last time.
	 * 
	 */
	private void connectionFailed() {
		this.authenticated = false;
		this.writer.close();
		this.reader.close();

		try {
			this.socket.close();
		} catch (IOException e1) {
			ALLog.e("In connectionFailed Close Socket Failed: " + e1.toString());
		}
		listener.onConnectLostEvent("", "");
	}

	private void connectionFailed(final String msg, final String conflictSid) {
		this.authenticated = false;
		if (this.writer != null) {
			this.writer.close();
		}

		if (this.reader != null) {
			this.reader.close();
		}

		if (this.socket != null)
			try {
				this.socket.close();
			} catch (IOException e1) {
				e1.printStackTrace();
				ALLog.e("In Callback Close Socket Failed: " + e1.toString());
			}
		listener.onConnectLostEvent(msg, conflictSid);
	}

	/**
	 * 获取当前是否已经登录到服务器
	 * 
	 * @return true 已经登录服务器。false 当前未登录服务器
	 */
	public boolean isAuthenticated() {
		return this.authenticated;
	}

	/**
	 * 获取当前是否有XMPP连接
	 * 
	 * @return true 已经建立XMPP连接。false 未建立XMPP连接
	 */
	public boolean isConnected() {
		if (socket != null) {
			return socket.isConnected();
		}
		return false;
	}

	/******************************* voip *************************************/

	/**
	 * 拨打voip电话
	 * 
	 * @param jid
	 *            被叫人jid
	 */
	public void makeVoipCall(String jid) {

		// MakeCallIQCreator m = new MakeCallIQCreator(jid, mData.getDomain());
		//
		// sendXmppData(m);
		//
		// m.release();
		// m = null;
		// try {
		// this.writer.write(m.getIQXml());
		// m.release();
		// m = null;
		// } catch (IOException e) {
		// // TODO Auto-generated catch block
		// e.printStackTrace();
		// }
		// ALXMLGenerator xml = new ALXMLGenerator();
		//
		// try {
		// xml.startTag("iq");
		//
		// xml.attribute("type", "get");
		// xml.attribute("to", "voip." + mData.getDomain());
		// xml.attribute("id", PackactIdGenerator.getPackactIdGenerator()
		// .getPackactId(8));
		//
		// xml.startTag("query");
		// xml.attribute("xmlns", "jabber:iq:voip:call");
		//
		// xml.startTag("jid");
		// xml.text(jid);
		// xml.endTag(); // end of jid
		//
		// xml.endTag(); // end of query
		// xml.endTag(); // end of iq
		//
		// this.writer.write(xml.getXml());
		//
		// } catch (final IOException e) {
		// // e.printStackTrace();
		// ALLog.e("makeVoipCall Failed: " + e.toString());
		// // 回调函数通知上层，连接已出现异常。
		// this.connectionFailed(e.toString());
		// }

	}

	/**
	 * 接听voip电话
	 * 
	 * @param sessionId
	 *            回话id
	 */
	public void acceptVoipCall(String sessionId) {

		ALXMLGenerator xml = new ALXMLGenerator();

		try {
			xml.startTag("iq");

			xml.attribute("type", "set");
			xml.attribute("to", "voip." + mData.getDomain());
			xml.attribute("id", PackactIdGenerator.getPackactIdGenerator()
					.getPackactId(8));

			xml.startTag("query");
			xml.attribute("xmlns", "jabber:iq:voip:response");

			xml.startTag("sessionid");
			xml.text(sessionId);
			xml.endTag(); // end of sessionid

			xml.endTag(); // end of query
			xml.endTag(); // end of iq

			this.writer.write(xml.getXml());

		} catch (final IOException e) {
			// e.printStackTrace();
			ALLog.e("acceptVoipCall Failed: " + e.toString());
			// 回调函数通知上层，连接已出现异常。
			this.connectionFailed(e.toString());
		}

	}

	/**
	 * 拒绝voip电话
	 * 
	 * @param sessionId
	 *            回话id
	 */
	public void rejectVoipCall(String sessionId) {

		ALXMLGenerator xml = new ALXMLGenerator();

		try {
			xml.startTag("iq");

			xml.attribute("type", "set");
			xml.attribute("to", "voip." + mData.getDomain());
			xml.attribute("id", PackactIdGenerator.getPackactIdGenerator()
					.getPackactId(8));

			xml.startTag("query");
			xml.attribute("xmlns", "jabber:iq:voip:reject");

			xml.startTag("sessionid");
			xml.text(sessionId);
			xml.endTag(); // end of sessionid

			xml.endTag(); // end of query
			xml.endTag(); // end of iq

			this.writer.write(xml.getXml());

		} catch (final IOException e) {
			// e.printStackTrace();
			ALLog.e("rejectVoipCall Failed: " + e.toString());
			// 回调函数通知上层，连接已出现异常。
			this.connectionFailed(e.toString());
		}

	}

	/**
	 * 挂断voip电话
	 * 
	 * @param sessionId
	 *            回话id
	 */
	public void handupVoipCall(String sessionId) {

		ALXMLGenerator xml = new ALXMLGenerator();

		try {
			xml.startTag("iq");

			xml.attribute("type", "set");
			xml.attribute("to", "voip." + mData.getDomain());
			xml.attribute("id", PackactIdGenerator.getPackactIdGenerator()
					.getPackactId(8));

			xml.startTag("query");
			xml.attribute("xmlns", "jabber:iq:voip:end");

			xml.startTag("sessionid");
			xml.text(sessionId);
			xml.endTag(); // end of sessionid

			xml.endTag(); // end of query
			xml.endTag(); // end of iq

			this.writer.write(xml.getXml());

		} catch (final IOException e) {
			// e.printStackTrace();
			ALLog.e("Send Feedback Failed: " + e.toString());
			// 回调函数通知上层，连接已出现异常。
			this.connectionFailed(e.toString());
		}

	}

	/**
	 * 上传voip网络状态
	 * 
	 * @param sessionId
	 *            回话id
	 */
	public void voipUploadNetState(String type) {

		ALXMLGenerator xml = new ALXMLGenerator();

		try {
			xml.startTag("iq");

			xml.attribute("type", "set");
			xml.attribute("to", "voip." + mData.getDomain());
			xml.attribute("id", PackactIdGenerator.getPackactIdGenerator()
					.getPackactId(8));

			xml.startTag("query");
			xml.attribute("xmlns", "jabber:iq:netstate:set");

			xml.startTag("type");
			xml.text(type);
			xml.endTag(); // end of type

			xml.endTag(); // end of query
			xml.endTag(); // end of iq

			this.writer.write(xml.getXml());

		} catch (final IOException e) {
			// e.printStackTrace();
			ALLog.e("voipUploadNetState Failed: " + e.toString());
			// 回调函数通知上层，连接已出现异常。
			this.connectionFailed(e.toString());
		}

	}

	/******************************* voip *************************************/

	/**
	 * 发送协议
	 * 
	 * @param str
	 */
	public void sendXmppData(final String str) {
		try {
			this.writer.write(str);

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void sendXmppData(final Object obj) {
		try {
			this.writer.write(obj.toString());

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

};
